<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Encrypt / decrypt message</title>
  <style>
  * {
    box-sizing: border-box;
  }
  
  body {
    font-family: Helvetica, Arial, sans-serif;
    line-height: 1.6;
    margin: 0;
    padding: 20px;
    background-color: #f5f5f5;
    color: #333;
    max-width: 800px;
    margin: 0 auto;
  }
  
  h1, h2 {
    margin-top: 0;
    color: #2c3e50;
  }
  
  .container {
    background-color: white;
    border-radius: 8px;
    padding: 25px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
    margin-bottom: 20px;
  }
  
  input, textarea {
    width: 100%;
    padding: 12px;
    margin-bottom: 15px;
    border: 1px solid #ddd;
    border-radius: 4px;
    font-size: 16px;
    font-family: Helvetica, Arial, sans-serif;
  }
  
  textarea {
    min-height: 150px;
    resize: vertical;
  }
  
  button {
    background-color: #3498db;
    border: none;
    color: white;
    padding: 12px 20px;
    text-align: center;
    text-decoration: none;
    display: inline-block;
    font-size: 16px;
    margin: 4px 2px;
    cursor: pointer;
    border-radius: 4px;
    transition: background-color 0.3s;
  }
  
  button:hover {
    background-color: #2980b9;
  }
  
  #encrypt-ui, #decrypt-ui {
    display: none;
  }
  
  .active {
    display: block !important;
  }
  
  .error {
    color: #e74c3c;
    margin-top: 10px;
  }
  
  .success {
    color: #2ecc71;
    margin-top: 10px;
  }
  
  #copy-button {
    background-color: #2ecc71;
  }
  
  #copy-button:hover {
    background-color: #27ae60;
  }
  
  #decrypt-result {
    background-color: #f8f9fa;
    padding: 15px;
    border-radius: 4px;
    border-left: 4px solid #3498db;
    margin-top: 20px;
    white-space: pre-wrap;
    word-break: break-word;
  }
  
  .url-display {
    word-break: break-all;
    padding: 10px;
    background-color: #f0f0f0;
    border-radius: 4px;
    margin: 15px 0;
    font-family: monospace;
  }

  .tabs {
    display: flex;
    margin-bottom: 20px;
  }
  
  .tab {
    padding: 10px 20px;
    background-color: #f0f0f0;
    cursor: pointer;
    border-radius: 4px 4px 0 0;
    margin-right: 5px;
  }
  
  .tab.active {
    background-color: #3498db;
    color: white;
  }
  </style>
</head>
<body>
  <h1>Encrypt / decrypt message</h1>
  
  <div class="tabs">
    <div class="tab" id="encrypt-tab">Encrypt a message</div>
    <div class="tab" id="decrypt-tab">Decrypt a message</div>
  </div>
  
  <div id="encrypt-ui" class="container">
    <h2>Encrypt your message</h2>
    <form id="encrypt-form">
      <div>
        <label for="encrypt-passphrase">Passphrase:</label>
        <input type="password" id="encrypt-passphrase" placeholder="Enter a secure passphrase" autocomplete="new-password" required>
      </div>
      <div>
        <label for="message">Message:</label>
        <textarea id="message" placeholder="Enter your secret message here..." required></textarea>
      </div>
      <button type="submit" id="encrypt-button">Encrypt message</button>
    </form>
    
    <div id="encrypt-result" style="display:none;">
      <h3>Encrypted message</h3>
      <div id="encrypted-text" class="url-display"></div>
      <p>Share this link:</p>
      <div id="share-url" class="url-display"></div>
      <button id="copy-button">Copy link to clipboard</button>
      <div id="copy-status"></div>
    </div>
    <p>Encrypt a message with a passphrase, send a link to someone and they can use that passphrase to decrypt your message.</p>
  </div>
  
  <div id="decrypt-ui" class="container">
    <h2>Decrypt a message</h2>
    <div id="decrypt-message-container">
      <p>This page contains an encrypted message.</p>
      <form id="decrypt-form">
        <div>
          <label for="decrypt-passphrase">Enter passphrase to decrypt:</label>
          <input type="password" id="decrypt-passphrase" placeholder="Enter the passphrase" autocomplete="new-password" required>
        </div>
        <button type="submit" id="decrypt-button">Decrypt message</button>
      </form>
      <div id="decrypt-error" class="error"></div>
      <div id="decrypt-result" style="display:none;"></div>
    </div>
  </div>

  <script type="module">
// Utility functions for encryption/decryption
const encryptionUtils = {
  async generateKey(passphrase, salt) {
    // Convert passphrase to key material
    const encoder = new TextEncoder();
    const passphraseData = encoder.encode(passphrase);
    
    // Use PBKDF2 to derive a key from the passphrase
    const importedKey = await crypto.subtle.importKey(
      'raw',
      passphraseData,
      { name: 'PBKDF2' },
      false,
      ['deriveBits', 'deriveKey']
    );
    
    // Derive an AES-GCM key using PBKDF2
    return crypto.subtle.deriveKey(
      {
        name: 'PBKDF2',
        salt: salt,
        iterations: 100000,
        hash: 'SHA-256'
      },
      importedKey,
      { name: 'AES-GCM', length: 256 },
      false,
      ['encrypt', 'decrypt']
    );
  },
  
  async encrypt(message, passphrase) {
    try {
      // Generate a random salt and IV
      const salt = crypto.getRandomValues(new Uint8Array(16));
      const iv = crypto.getRandomValues(new Uint8Array(12));
      
      // Generate encryption key from passphrase
      const key = await this.generateKey(passphrase, salt);
      
      // Encrypt the message
      const encoder = new TextEncoder();
      const messageData = encoder.encode(message);
      
      const encryptedData = await crypto.subtle.encrypt(
        { name: 'AES-GCM', iv },
        key,
        messageData
      );
      
      // Combine salt, IV, and encrypted data into one array
      const combinedData = new Uint8Array(salt.length + iv.length + encryptedData.byteLength);
      combinedData.set(salt, 0);
      combinedData.set(iv, salt.length);
      combinedData.set(new Uint8Array(encryptedData), salt.length + iv.length);
      
      // Convert to base64 for easy URL sharing
      return btoa(String.fromCharCode.apply(null, combinedData));
    } catch (error) {
      console.error('Encryption error:', error);
      throw new Error('Failed to encrypt the message');
    }
  },
  
  async decrypt(encryptedMessage, passphrase) {
    try {
      // Convert base64 back to Uint8Array
      const binaryString = atob(encryptedMessage);
      const bytes = new Uint8Array(binaryString.length);
      for (let i = 0; i < binaryString.length; i++) {
        bytes[i] = binaryString.charCodeAt(i);
      }
      
      // Extract salt, IV, and encrypted data
      const salt = bytes.slice(0, 16);
      const iv = bytes.slice(16, 28);
      const encryptedData = bytes.slice(28);
      
      // Generate decryption key from passphrase
      const key = await this.generateKey(passphrase, salt);
      
      // Decrypt the message
      const decryptedData = await crypto.subtle.decrypt(
        { name: 'AES-GCM', iv },
        key,
        encryptedData
      );
      
      // Convert decrypted data to string
      const decoder = new TextDecoder();
      return decoder.decode(decryptedData);
    } catch (error) {
      console.error('Decryption error:', error);
      throw new Error('Failed to decrypt the message. Wrong passphrase?');
    }
  }
};

// UI management functions
const uiManager = {
  init() {
    // Get UI elements
    this.encryptTab = document.getElementById('encrypt-tab');
    this.decryptTab = document.getElementById('decrypt-tab');
    this.encryptUI = document.getElementById('encrypt-ui');
    this.decryptUI = document.getElementById('decrypt-ui');
    this.encryptForm = document.getElementById('encrypt-form');
    this.decryptForm = document.getElementById('decrypt-form');
    this.copyButton = document.getElementById('copy-button');
    
    // Set up event listeners
    this.encryptTab.addEventListener('click', () => this.showTab('encrypt'));
    this.decryptTab.addEventListener('click', () => this.showTab('decrypt'));
    this.encryptForm.addEventListener('submit', this.handleEncrypt.bind(this));
    this.decryptForm.addEventListener('submit', this.handleDecrypt.bind(this));
    this.copyButton.addEventListener('click', this.copyToClipboard.bind(this));
    
    // Check URL fragment for encrypted message
    this.checkUrlFragment();
  },
  
  showTab(tab) {
    if (tab === 'encrypt') {
      this.encryptTab.classList.add('active');
      this.decryptTab.classList.remove('active');
      this.encryptUI.classList.add('active');
      this.decryptUI.classList.remove('active');
    } else {
      this.encryptTab.classList.remove('active');
      this.decryptTab.classList.add('active');
      this.encryptUI.classList.remove('active');
      this.decryptUI.classList.add('active');
    }
  },
  
  checkUrlFragment() {
    const fragment = window.location.hash.substring(1);
    if (fragment) {
      // We have an encrypted message in the URL
      this.showTab('decrypt');
      // Focus on passphrase input for better UX
      setTimeout(() => {
        document.getElementById('decrypt-passphrase').focus();
      }, 100);
    } else {
      // No encrypted message, default to encrypt view
      this.showTab('encrypt');
    }
  },
  
  async handleEncrypt(event) {
    event.preventDefault();
    
    const passphrase = document.getElementById('encrypt-passphrase').value;
    const message = document.getElementById('message').value;
    
    if (!passphrase || !message) {
      alert('Please enter both a passphrase and message.');
      return;
    }
    
    try {
      const encryptedMessage = await encryptionUtils.encrypt(message, passphrase);
      
      // Display encrypted message
      document.getElementById('encrypted-text').textContent = encryptedMessage;
      
      // Update URL with encrypted message
      const shareUrl = `${window.location.origin}${window.location.pathname}#${encryptedMessage}`;
      document.getElementById('share-url').textContent = shareUrl;
      
      // Show result section
      document.getElementById('encrypt-result').style.display = 'block';
      
      // Update URL fragment
      window.location.hash = encryptedMessage;
    } catch (error) {
      alert(error.message);
    }
  },
  
  async handleDecrypt(event) {
    event.preventDefault();
    
    const fragment = window.location.hash.substring(1);
    const passphrase = document.getElementById('decrypt-passphrase').value;
    
    if (!passphrase) {
      alert('Please enter the passphrase.');
      return;
    }
    
    if (!fragment) {
      alert('No encrypted message found. Please check your URL.');
      return;
    }
    
    try {
      const decryptedMessage = await encryptionUtils.decrypt(fragment, passphrase);
      
      // Display decrypted message
      const decryptResult = document.getElementById('decrypt-result');
      decryptResult.textContent = decryptedMessage;
      decryptResult.style.display = 'block';
      
      // Clear any previous errors
      document.getElementById('decrypt-error').textContent = '';
    } catch (error) {
      document.getElementById('decrypt-error').textContent = error.message;
      document.getElementById('decrypt-result').style.display = 'none';
    }
  },
  
  copyToClipboard() {
    const shareUrl = document.getElementById('share-url').textContent;
    
    navigator.clipboard.writeText(shareUrl)
      .then(() => {
        const statusElement = document.getElementById('copy-status');
        statusElement.textContent = 'Link copied to clipboard!';
        statusElement.className = 'success';
        
        setTimeout(() => {
          statusElement.textContent = '';
        }, 3000);
      })
      .catch(err => {
        document.getElementById('copy-status').textContent = 'Failed to copy: ' + err;
        document.getElementById('copy-status').className = 'error';
      });
  }
};

// Initialize the application
document.addEventListener('DOMContentLoaded', () => {
  uiManager.init();
});
  </script>
</body>
</html>